/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>

#include <dx/dx.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "edf.h"

/* prototypes
 */

static int tohex( char c );
static Error set_lines( struct finfo *fp );

#define COMMENTCHAR '#' /* rest of current input line ignored */

#if 1 /* use the macros for performance instead of a func call */

#define input() ( pp->byteoffset++, getc( fp->fd ) )
#define pushback( c ) ( ungetc( c, fp->fd ), --pp->byteoffset )

#else

/* i was using these until i got the above macros to work - the comma
 * was screwing things up until i parenthesized them.
 */
static char input( FILE *fp, int *offset )
{
  *offset++;
  return getc( fp );
}

static void pushback( char c, FILE *fp, int *offset )
{
  ungetc( c, fp );
  *offset--;
}
#endif

/*
 * return the next token from the input stream.
 *
 * input is a pointer to a file structure, which contains an open file
 * pointer, current offset into the buffer, current lineno and input buffer.
 *
 * output is a pointer to a token structure, which contains a flag to
 * distinquish different classes of input (numbers, keywords, variables and
 * errors), and the token value which is interpeted differently depending on
 * the class of the input.
 *
 * CODE WHICH NEEDS TO BE ADDED:
 *  support for [ x y z ] three vector numbers (also [x,y,z]?)
 *  \\ at end of line to continue strings
 */
Error _dxflexinput( struct finfo *fp )
{
  char nextc, *cp;
  int value;
  int incomment = 0;
  struct tinfo *tp;
  struct pinfo *pp;

  /* things for number conversions. */
  int onesign, onedot, oneexp;
  int isfloat, isdouble;
  char *pokehere;
  int done;
  double fvalue;

  if ( !fp )
    return ERROR;

  tp = &fp->t;
  pp = &fp->p;

  /* if you are already at the end of the header or end of file,
   *  don't read further
   */
  if ( tp->class == ENDOFHEADER )
    return OK;

  if ( tp->class == KEYWORD && tp->token.id == KW_END )
    return OK;

  /* clear tempbuf count, and keep track of what line we started on.
   */
  pp->incount = 0;
  _dxfsetprevline( fp );

  /* get the next token from the input file.
   */
  while ( 1 )
  {

    /* next character
     */
    nextc = input();

    /* end of input?
     */
    if ( feof( fp->fd ) )
    {
      tp->class = ENDOFHEADER;
      return OK;
    }

    /* new line?
     */
    if ( nextc == '\n' )
    {
      fp->p.lineno++;

      /* set byteoffset vs. line num here */
      if ( fp->p.lineno > fp->p.l.lcount )
        set_lines( fp );

      incomment = 0;

      /* return at start of next line? */
      if ( pp->tonextline )
      {
        tp->class = PUNCT;
        tp->subclass = NEWLINE;
        return OK;
      }

      continue;
    }

    /* already in a comment?
     */
    if ( incomment )
      continue;

    /* start of a new comment?
     */
    if ( nextc == COMMENTCHAR )
    {
      incomment = 1;
      continue;
    }

    /* other whitespace?
     */
    if ( isspace( nextc ) )
      continue;

    /* binary?
     */
    if ( !isprint( nextc ) )
    {
      pushback( nextc );
      tp->class = NOTASCII;
      return OK;
    }

    /* comma or semicolon?
     * (can be used as number separators)
     */
    if ( nextc == ',' || nextc == ';' )
    {

      if ( pp->skippunct )
        continue;

      tp->class = PUNCT;
      tp->subclass = ( nextc == ',' ) ? COMMA : SEMICOLON;
      return OK;
    }

    /* quoted string?
     *  copy until matching close quote.  multiline strings must have
     *  newlines escaped.  \c and \Xxx and \ooo char codes are ok.
     */
    if ( nextc == '"' || nextc == '\'' )
    {

      /* skip first char (the delimiter) and get the first one from
       *  the string.
       */
      pp->delim = nextc;
      cp = pp->inbuf;
      nextc = input();

      /* until matching delimiter or the buffer fills, read chars */
      while ( ( nextc != pp->delim ) && ( cp < &pp->inbuf[MAXBUF - 1] ) )
      {
        /* escape */
        if ( nextc == '\\' )
        {
          int i;
          uint num;
          char tc;
          switch ( nextc = input() )
          {
            /* standard ascii escape chars */
            case 'a':
              *cp++ = '\a';
              break;
            case 'b':
              *cp++ = '\b';
              break;
            case 'f':
              *cp++ = '\f';
              break;
            case 'n':
              *cp++ = '\n';
              break;
            case 'r':
              *cp++ = '\r';
              break;
            case 't':
              *cp++ = '\t';
              break;
            case 'v':
              *cp++ = '\v';
              break;
            case '\\':
              *cp++ = '\\';
              break;
            case '"':
              *cp++ = '"';
              break;
            case '\'':
              *cp++ = '\'';
              break;
            case '?':
              *cp++ = '?';
              break;
            /* hex codes - 0, 1 or 2 digits */
            case 'x':
              tc = input();
              if ( !isxdigit( tc ) )
              {
                pushback( tc );
                *cp++ = 'x';
                break;
              }
              num = tohex( tc );
              tc = input();
              if ( !isxdigit( tc ) )
                pushback( tc );
              else
              {
                num <<= 4;
                num += tohex( tc );
              }
              *cp++ = (char)num;
              break;
            /* octal codes - 1, 2 or 3 digits */
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
              num = ( uint )( nextc ) - (uint)'0';
              for ( i = 0; i < 2; i++ )
              {
                tc = input();
                if ( !isdigit( tc ) || tc == '8' || tc == '9' )
                  pushback( tc );
                else
                {
                  num <<= 3;
                  num += ( uint )( tc ) - (uint)'0';
                }
              }
              *cp++ = (char)num;
              break;
            /* end of file */
            case(char)EOF:
              goto endofquote;
            /* escaped end of line */
            case '\n':
              fp->p.lineno++;
              /* set byteoffset vs. line num here */
              if ( fp->p.lineno > fp->p.l.lcount )
                set_lines( fp );

              break;
            /* any other char */
            default:
              *cp++ = nextc;
              break;
          }
        }
        else if ( nextc == (char)EOF )
          goto endofquote;

        else if ( nextc == '\n' )
          goto endofquote;

        else
          *cp++ = nextc;

        /* read next input char */
        nextc = input();
      }

      /* terminate string in buffer */
      *cp = '\0';

    endofquote:
      /* if the current character isn't the close delimiter,
       *  there is an error.
       */
      if ( nextc != pp->delim )
      {
        tp->class = LEXERROR;
        if ( cp >= &pp->inbuf[MAXBUF - 1] )
          tp->subclass = BADLENGTH;
        else
          tp->subclass = BADQUOTE;
        return ERROR;
      }

      /* if you just want the string without putting it in the
       * dictionary (like for string data arrays), return ok
       * but with an invalid dictionary id.  before you call
       * this routine again you must get the data from the token
       * buffer because it will get overwritten by the next string.
       * also set char count here because the string data routine
       * needs it.
       */
      if ( pp->nodict )
      {
        tp->class = STRING;
        tp->token.id = BADINDEX;
        pp->incount = cp - pp->inbuf; /* plus or minus? */
        return OK;
      }

      value = _dxfputdict( fp->d, pp->inbuf );
      if ( value != BADINDEX )
      {
        tp->class = STRING;
        tp->token.id = value;
        return OK;
      }

      tp->class = LEXERROR;
      tp->subclass = BADINPUT;
      return ERROR;
    }

/* other delimited string?
 */
#if 0 
	/* there are currently no other delimiters defined, but if we
	 *  end up needing brackets or parens, here's where the code goes.
	 */
	if (nextc == '<' || nextc == '(' || nextc == '[' || nextc == '{') {
	    switch (nextc) {
	      case '<': pp->delim = '>'; tp->subclass = ANGLE; break;
	      case '(': pp->delim = ')'; tp->subclass = PAREN; break;
	      case '[': pp->delim = ']'; tp->subclass = SQUARE; break;
	      case '{': pp->delim = '}'; tp->subclass = CURLY; break;
	    }

	    /* skip first char (the delimiter) and get the first one from
	     *  the string.
	     */
	    pp->delim = nextc;
	    cp = pp->inbuf;
	    nextc = input();

	    /* until matching delimiter or the buffer fills, read chars */
	    while ((nextc != pp->delim) && (cp < &pp->inbuf[MAXBUF-1])) {
		*cp++ = nextc;
		if (nextc == '\n') {
		    fp->p.lineno++;
		    /* set byteoffset vs. line num here */
		    if (fp->p.lineno > fp->p.l.lcount)
			set_lines(fp);
		}
		nextc = input();
	    }
	    *cp = '\0';
 
	    if (nextc != pp->delim) {
		tp->class = LEXERROR;
		tp->subclass = BADBRACKET;
		return ERROR;
	    }
 
	    if (pp->nodict) {
		tp->class = BRACKET;
		tp->token.id = BADINDEX;
		pp->incount = cp - pp->inbuf;  
		return OK;
	    }

	    /* change this to make the class whatever the inside of the
	     * (), {}, <> or [] means.
	     */
	    value = _dxfputdict(fp->d, pp->inbuf);
	    if(value != BADINDEX) {
		tp->class = BRACKET;
		/* the subclass has already been set above */
		tp->token.id = value;
		return OK;
	    }
 
	    tp->class = LEXERROR;
	    tp->subclass = BADINPUT;
	    return ERROR;
	}
#endif

/* here it starts to get tricky.  up to now i know what a token
 *  is by looking at the first character.  but identifiers can
 *  start with a number.  if the first char is a number, try to
 *  parse it as a number until you get something which isn't
 *  legal as a number.  here's where knowing if you are looking
 *  for a string, number or either would be helpful.
 */

/* here's where con ed shut off the power on me and i lost
 *  about a page of editing.  drat them.
 */

#define Is( c ) ( nextc == c )
#define IsNot( c ) ( nextc != c )

    /* number?  (base 10 only, but exponential notation is ok).
     */
    if ( isdigit( nextc ) || Is( '-' ) || Is( '+' ) || Is( '.' ) )
    {
      onesign = 0;
      onedot = 0;
      oneexp = 0;
      isfloat = 0;
      isdouble = 0;
      pokehere = NULL;
      done = 0;
      fvalue = 0.0;

      /* if the next thing should be a string, don't try to parse
       *  it as number - go directly to the string code.
       */
      if ( pp->mustmatch == STRING )
        goto notnum;

      cp = pp->inbuf;
      while ( !done )
      {
        switch ( nextc )
        {
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            break;
          case '-':
          case '+':
            if ( onesign )
            {
              tp->class = LEXERROR;
              tp->subclass = BADNUMBER;
              return ERROR;
            }
            onesign++;
            break;
          case '.':
            if ( onedot )
            {
              tp->class = LEXERROR;
              tp->subclass = BADNUMBER;
              return ERROR;
            }
            onedot++;
            isfloat++;
            break;
          case 'd':
          case 'D':
            pokehere = cp;
            isdouble++;
          /* fall thru */
          case 'e':
          case 'E':
            if ( oneexp )
            {
              tp->class = LEXERROR;
              tp->subclass = BADNUMBER;
              return ERROR;
            }
            oneexp++;
            onesign = 0;
            isfloat++;
            break;
          default:
            done++;
            continue;
        }
        *cp++ = nextc;
        nextc = input();
      }

      /* if there is something which is part of the string but is
       *  not a digit, this must not be a number.  reset the ptr
       *  and try again.
       */
      /* NEEDS FIX !!!   should be isalpha? */
      if ( !isspace( nextc ) )
      {
        /* allow a few things besides whitespace to terminate the #.
         * anything else is assumed to be part of an unquoted string.
         */
        if ( IsNot( COMMENTCHAR ) && IsNot( ',' ) && IsNot( ';' ) &&
             IsNot( (char)EOF ) )
        {
          *cp++ = nextc;
          pp->incount = cp - pp->inbuf;
          nextc = pp->inbuf[0];
          goto notnum;
        }
      }
      pushback( nextc );

      *cp = '\0';

      /* if we don't care about the value, don't do the conversion.
       */
      if ( pp->skipnum )
      {
        tp->class = NUMBER;
        return OK;
      }

      tp->class = NUMBER;
      if ( isfloat )
      {
        if ( isdouble )
          *pokehere = 'e';
        fvalue = atof( pp->inbuf );
        if ( !isdouble && ( ( fabs( fvalue ) == 0.0 ) ||
                            ( ( fabs( fvalue ) > DXD_MIN_FLOAT ) &&
                              ( fabs( fvalue ) < DXD_MAX_FLOAT ) ) ) )
        {
          tp->token.f = (float)fvalue;
          tp->subclass = FLOAT;
        }
        else
        {
          tp->token.d = fvalue;
          tp->subclass = DOUBLE;
        }
      }
      else
      {
        value = atoi( pp->inbuf );
        tp->token.i = value;
        tp->subclass = INTEGER;
      }
      return OK;
    }

  notnum:

    /* keyword or unquoted string?
     */
    if ( isprint( nextc ) )
    {
      int bc = 1;

      /* since we can get here from a partially parsed number,
       *  this has to be able to get the next char either from
       *  the token buffer or from the input file.
       */
      cp = &pp->inbuf[pp->incount > 0 ? pp->incount - 1 : 0];

      while ( !isspace( nextc ) && ( cp < &pp->inbuf[MAXBUF - 1] ) )
      {
        if ( Is( ',' ) )
          break;
        if ( bc < pp->incount )
        {
          nextc = pp->inbuf[bc];
          bc++;
        }
        else
        {
          *cp++ = nextc;
          nextc = input();
        }
      }

      *cp = '\0';

      if ( cp >= &pp->inbuf[MAXBUF - 1] )
      {
        pushback( nextc );
        tp->class = LEXERROR;
        tp->subclass = BADLENGTH;
        return ERROR;
      }

      /* put the separator character back for next time
       */
      pushback( nextc );

      /* check for keyword; return keyword id */
      value = _dxfiskeyword( fp->d, pp->inbuf );
      if ( value != KW_NULL )
      {
        tp->token.id = value;
        tp->class = KEYWORD;

        /* some special case code here for end of header */
        if ( value == KW_END )
        {
          while ( nextc != '\n' )
          {
            if ( feof( fp->fd ) || !isprint( nextc ) )
              break;

            nextc = input();
          }
          /* eat the newline */
          if ( nextc == '\n' )
            input();
        }

        return OK;
      }

      if ( pp->nodict )
      {
        tp->class = IDENTIFIER;
        tp->token.id = BADINDEX;
        return OK;
      }

      /* a filename, perhaps. */
      /* string identifier - add it to the dictionary */
      value = _dxfputdict( fp->d, pp->inbuf );
      if ( value != BADINDEX )
      {
        tp->class = IDENTIFIER;
        tp->token.id = value;
        return OK;
      }

      /* not a keyword or identifier we recognize - return error */
      tp->class = LEXERROR;
      tp->subclass = BADINPUT;
      return ERROR;
    }

    /* not anything we recognize - return error.  don't try to skip it
     *  or recover.
     */
    tp->class = LEXERROR;
    tp->subclass = BADINPUT;
    return ERROR;
  }

  /* notreached */
}

/* debug and errors
 */
char *_dxfprtoken( struct tinfo *t, struct dict *d )
{
  static char cbuf[512];

  switch ( t->class )
  {
    case ENDOFHEADER:
      sprintf( cbuf, "end of input" );
      break;
    case NUMBER:
      switch ( t->subclass )
      {
        case INTEGER:
          sprintf( cbuf, "integer: %d", t->token.i );
          break;
        case FLOAT:
          sprintf( cbuf, "float: %f", t->token.f );
          break;
        case DOUBLE:
          sprintf( cbuf, "double: %g", t->token.d );
          break;
        case CHAR:
          sprintf( cbuf, "byte: %x", (unsigned int)t->token.c );
          break;
        default:
          sprintf( cbuf, "bad number" );
          break;
      }
      break;
    case KEYWORD:
      sprintf( cbuf, "keyword '%s'", _dxflookkeyword( t->token.id ) );
      break;
    case IDENTIFIER:
      sprintf( cbuf, "string '%s'", _dxfdictname( d, t->token.id ) );
      break;
    case STRING:
      sprintf( cbuf, "string '%s'", _dxfdictname( d, t->token.id ) );
      break;
    case LEXERROR:
      switch ( t->subclass )
      {
        case BADINPUT:
          sprintf( cbuf, "bad input" );
          break;
        case BADQUOTE:
          sprintf( cbuf, "missing quote" );
          break;
        case BADNUMBER:
          sprintf( cbuf, "bad number" );
          break;
        case BADCOMMA:
          sprintf( cbuf, "multiple commas" );
          break;
        case BADBRACKET:
          sprintf( cbuf, "missing bracket" );
          break;
        case BADLENGTH:
          sprintf( cbuf, "line too long" );
          break;
        default:
          sprintf( cbuf, "unknown error" );
          break;
      }
      break;
    default:
      sprintf( cbuf, "bad input" );
      break;
  }

  return cbuf;
}

/* initialise the parsing routine.
 */
Error _dxfinitparse( struct finfo *f )
{
  f->p.inbuf = (char *)DXAllocateLocal( MAXBUF );
  if ( !f->p.inbuf )
    return ERROR;

  f->p.lineno = 1;

  /* init line buffer here? */
  return _dxflexinput( f );
}

/* reset to start of file.
 */
Error _dxfresetparse( struct finfo *f )
{
  fseek( f->fd, 0, 0 );
  f->p.lineno = 1;
  f->t.class = -1;

  return _dxflexinput( f );
}

/* reset to a specific byte offset.
 */
Error _dxfsetparse( struct finfo *f, long byteoffset, int lines, int lineno )
{
  fseek( f->fd, byteoffset, 0 );
  if ( lines )
    f->p.lineno = lineno;

  f->t.class = -1;

  return _dxflexinput( f );
}

/* clean up any mem allocated for pinfo struct.
 */
void _dxfdeleteparse( struct finfo *f )
{
  if ( !f )
    return;

  if ( f->p.inbuf )
    DXFree( f->p.inbuf );

  /* free line buffer here */
}

/* set the type of the next thing to be parsed.
 */
void _dxfsetnexttype( struct finfo *f, int type )
{
  f->p.mustmatch = type;
}

/* set the type of the next thing to be parsed.
 */
void _dxfsetskipnum( struct finfo *f, int type )
{
  f->p.skipnum = type;
}

/* set the starting line number of the current object definition
 * (used for error messages)
 */
void _dxfsetstartline( struct finfo *f )
{
  f->p.startlineno = f->p.lineno;
}

int _dxfgetstartline( struct finfo *f )
{
  return f->p.startlineno;
}

void _dxfsetprevline( struct finfo *f )
{
  f->p.prevlineno = f->p.lineno;
}

int _dxfgetprevline( struct finfo *f )
{
  return f->p.prevlineno;
}

int _dxfgetlineno( struct finfo *f )
{
  return f->p.lineno;
}

void _dxfsetdictstringmode( struct finfo *f, int value )
{
  f->p.nodict = value;
}

Error _dxfgetstringnodict( struct finfo *f )
{
  return get_string( f, NULL );
}

char *_dxfgetstringinfo( struct finfo *f, int *len )
{
  int class;

  next_class( f, &class );
  if ( class != STRING )
    return NULL;

  if ( len )
    *len = f->p.incount;

  return f->p.inbuf;
}

/* only skips whitespace and returns after the next newline
 */
Error _dxfnextline( struct finfo *f )
{
  int rc;

  f->p.tonextline = 1;
  rc = _dxflexinput( f );
  f->p.tonextline = 0;

  return rc;
}

Error _dxfendnotascii( struct finfo *f )
{
  return _dxflexinput( f );
}

/*
 * set offset of end of header --  first character after the newline
 *  following the 'end' keyword.
 */
Error _dxfset_headerend( struct finfo *f )
{
  f->headerend = ftell( f->fd );
  return OK;
}

static Error set_lines( struct finfo *f )
{
  return OK;
}

/* token info stuff */

/* return the info about the current token without reading ahead.
 *  sets class, subclass and id if the pointers are not null.
 */
Error _dxfnexttoken( struct finfo *f, int *class, int *subclass, int *id )
{
#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
#endif

  if ( class )
    *class = f->t.class;

  if ( subclass )
    *subclass = f->t.subclass;

  if ( id )
    *id = f->t.token.id;

  return f->t.class == LEXERROR ? ERROR : OK;
}

/* return OK if all the input parms specified match.
 *  does NOT read ahead.
 */
Error _dxfisnexttoken( struct finfo *f, int *class, int *subclass, int *id )
{
#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
#endif

  if ( class && *class != f->t.class )
    return ERROR;

  if ( subclass && *subclass != f->t.subclass )
    return ERROR;

  if ( id && *id != f->t.token.id )
    return ERROR;

  return OK;
}

/* return id of next thing if the classes and subclasses match.
 *  (in contrast to the next routine which should be used if any
 *  subclass is ok and you just need to know what it is)
 */
Error _dxfmatchsaysub( struct finfo *f, int class, int subclass, int *id )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for class %d, subclass %d", class, subclass );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( ( f->t.class == class ) && ( f->t.subclass == subclass ) )
  {

    if ( id )
      *id = f->t.token.id;

    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

/* return subclass and id of next thing if the class matches.
 *  (in contrast to the previous routine which should be used if you
 *  only expect a specific subclass here).
 */
Error _dxfmatchanysub( struct finfo *f, int class, int *subclass, int *id )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for class %d", class );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == class )
  {

    if ( subclass )
      *subclass = f->t.subclass;

    if ( id )
      *id = f->t.token.id;

    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

/* return subclass and numeric value
 */
Error _dxfmatchnumber( struct finfo *f, int *subclass, Token *id )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for number" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER )
  {

    if ( subclass )
      *subclass = f->t.subclass;

    if ( id )
      *id = f->t.token;

    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

/* return ok if next thing matches both class and id match.
 *  parse ahead unless noparse flag is set.
 */
Error _dxfmatchid( struct finfo *f, int class, int id, int noparse )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for class %d, id %d", class, id );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( ( f->t.class == class ) && ( f->t.token.id == id ) )
  {

    if ( !noparse )
      rc = _dxflexinput( f );

    return rc;
  }

  return ERROR;
}

/* read tokens until either another object keyword or end keyword or error.
 */
Error _dxfskip_object( struct finfo *f )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for next object token" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  f->p.skipnum = 1;
  while ( rc != ERROR )
  {
    if ( f->t.class != KEYWORD && f->t.class != ENDOFHEADER )
    {
      rc = _dxflexinput( f );
      continue;
    }

    if ( f->t.class == ENDOFHEADER )
    {
      f->p.skipnum = 0;
      return OK;
    }

    if ( f->t.token.id != KW_OBJECT && f->t.token.id != KW_END )
    {
      rc = _dxflexinput( f );
      continue;
    }

    f->p.skipnum = 0;
    return rc;
  }

  f->p.skipnum = 0;
  return ERROR;
}

/* read tokens until either another object keyword or
 *  attribute keyword or end keyword or error.
 */
Error _dxfskip_object_or_attr( struct finfo *f )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for next object or attribute token" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  f->p.skipnum = 1;
  while ( rc != ERROR )
  {
    if ( f->t.class != KEYWORD && f->t.class != ENDOFHEADER )
    {
      rc = _dxflexinput( f );
      continue;
    }

    if ( f->t.class == ENDOFHEADER )
    {
      f->p.skipnum = 0;
      return OK;
    }

    if ( f->t.token.id != KW_OBJECT && f->t.token.id != KW_ATTRIBUTE &&
         f->t.token.id != KW_END )
    {
      rc = _dxflexinput( f );
      continue;
    }

    f->p.skipnum = 0;
    return rc;
  }

  f->p.skipnum = 0;
  return ERROR;
}

/* some routines to promote ints to floats, chars to ints, etc.
 */
Error _dxfmatchbyte( struct finfo *f, byte *cp )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for a byte" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    if ( f->t.token.i > DXD_MAX_BYTE || f->t.token.i < DXD_MIN_BYTE )
      return ERROR;

    *cp = (byte)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchubyte( struct finfo *f, ubyte *cp )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for an unsigned byte" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    if ( f->t.token.i > DXD_MAX_UBYTE || f->t.token.i < 0 )
      return ERROR;

    *cp = (ubyte)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchshort( struct finfo *f, short *sip )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for a short int" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    if ( f->t.token.i > DXD_MAX_SHORT || f->t.token.i < DXD_MIN_SHORT )
      return ERROR;

    *sip = (short)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchushort( struct finfo *f, ushort *sip )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for an unsigned short int" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    if ( f->t.token.i > DXD_MAX_USHORT || f->t.token.i < 0 )
      return ERROR;

    *sip = (short)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchint( struct finfo *f, int *ip )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for an integer" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    *ip = f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchuint( struct finfo *f, uint *ip )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for an unsigned integer" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    *ip = (uint)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchfloat( struct finfo *f, float *fp )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for a float" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == FLOAT )
  {
    *fp = f->t.token.f;
    rc = _dxflexinput( f );
    return rc;
  }

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    *fp = (float)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

Error _dxfmatchdouble( struct finfo *f, double *dp )
{
  Error rc = OK;

#if DEBUG_MSG
  if ( DXQueryDebug( "T" ) )
  {
    DXDebug( "T", "looking for a double" );
    DXDebug( "T", "last token = %s", _dxfprtoken( &f->t, f->d ) );
  }
#endif

  if ( f->t.class == NUMBER && f->t.subclass == DOUBLE )
  {
    *dp = f->t.token.d;
    rc = _dxflexinput( f );
    return rc;
  }

  if ( f->t.class == NUMBER && f->t.subclass == FLOAT )
  {
    *dp = (double)f->t.token.f;
    rc = _dxflexinput( f );
    return rc;
  }

  if ( f->t.class == NUMBER && f->t.subclass == INTEGER )
  {
    *dp = (double)f->t.token.i;
    rc = _dxflexinput( f );
    return rc;
  }

  return ERROR;
}

/* check 'c' before calling this with isxdigit() because this does no
 * error checking.
 */
static int tohex( char c )
{
  /* A-F */
  if ( isupper( c ) )
    return 10 + ( uint )( c ) - (uint)'A';

  /* a-f */
  if ( islower( c ) )
    return 10 + ( uint )( c ) - (uint)'a';

  /* 0-9 */
  return ( uint )( c ) - (uint)'0';
}
